/**
 * using for the transform in the 3d space.
 * reference with esMatrix
 */

slMatrix = function()
{
	// create a 2D 4*4 array
	this.m = new Array(4);
	for( var i=0; i < 4; ++i )
	{
		this.m[i] = new Array(4);
	}
};

function slMatrixSet( left, right )
{
	for(var i = 0; i < 4; ++i )
	{
		for( var j = 0; j < 4; ++j )
		{
			left.m[i][j] = right.m[i][j];
		}
	}
}

//Return the matrix as a flat Float32Array for
//loading as a uniform
function slMatrixAsFloat32Array(mat)
{    
	 var result = new Float32Array(16);
	 for ( var i = 0; i < 4; i++ )
	 {
	     for ( var j = 0; j < 4; j++ )
	     {
	         result[i * 4 + j] = mat.m[i][j];
	     }
	 }
	 return result;
}


function slScale( result, sx, sy, sz )
{
	result.m[0][0] *= sx;
	result.m[0][1] *= sx;
	result.m[0][2] *= sx;
	result.m[0][3] *= sx;

	result.m[1][0] *= sy;
	result.m[1][1] *= sy;
	result.m[1][2] *= sy;
	result.m[1][3] *= sy;

	result.m[2][0] *= sz;
	result.m[2][1] *= sz;
	result.m[2][2] *= sz;
	result.m[2][3] *= sz;
}

function slTranslate( result, tx, ty, tz )
{
    result.m[3][0] += (result.m[0][0] * tx + result.m[1][0] * ty + result.m[2][0] * tz);
    result.m[3][1] += (result.m[0][1] * tx + result.m[1][1] * ty + result.m[2][1] * tz);
    result.m[3][2] += (result.m[0][2] * tx + result.m[1][2] * ty + result.m[2][2] * tz);
    result.m[3][3] += (result.m[0][3] * tx + result.m[1][3] * ty + result.m[2][3] * tz);
}

function slMatrixMultiply( result, matA, matB )
{
	var tmp = new slMatrix();
	var i;
	for(i=0; i < 4; i++ )
	 {
        tmp.m[i][0] =	(matA.m[i][0] * matB.m[0][0]) +
                        (matA.m[i][1] * matB.m[1][0]) +
                        (matA.m[i][2] * matB.m[2][0]) +
                        (matA.m[i][3] * matB.m[3][0]) ;

        tmp.m[i][1] =	(matA.m[i][0] * matB.m[0][1]) +
                        (matA.m[i][1] * matB.m[1][1]) +
                        (matA.m[i][2] * matB.m[2][1]) +
                        (matA.m[i][3] * matB.m[3][1]) ;

        tmp.m[i][2] =	(matA.m[i][0] * matB.m[0][2]) +
                        (matA.m[i][1] * matB.m[1][2]) +
                        (matA.m[i][2] * matB.m[2][2]) +
                        (matA.m[i][3] * matB.m[3][2]) ;

        tmp.m[i][3] =	(matA.m[i][0] * matB.m[0][3]) +
                        (matA.m[i][1] * matB.m[1][3]) +
                        (matA.m[i][2] * matB.m[2][3]) +
                        (matA.m[i][3] * matB.m[3][3]) ;
    }

    for (i = 0; i < 4; i++)
    {
        for (var j = 0; j < 4; j++)
        {
            result.m[i][j] = tmp.m[i][j];
        }
    }
}

function slMatrixLoadIdentity( result )
{
	for( var i = 0; i < 4; i++ )
	{
		for(var j = 0; j < 4; j++ )
		{
			if( i == j )
			{
				result.m[i][j] = 1.0;
			}
			else
			{
				result.m[i][j] = 0.0;
			}
		}
	}
}

function slRotate( result, x, y, z, angle )
{
	  var sinAngle, cosAngle;
	   var mag = Math.sqrt(x * x + y * y + z * z);

	   sinAngle = Math.sin ( angle * Math.PI / 180.0 );
	   cosAngle = Math.cos ( angle * Math.PI / 180.0 );
	   if ( mag > 0.0 )
	   {
	      var xx, yy, zz, xy, yz, zx, xs, ys, zs;
	      var oneMinusCos;
	      var rotMat = new slMatrix();

	      x /= mag;
	      y /= mag;
	      z /= mag;

	      xx = x * x;
	      yy = y * y;
	      zz = z * z;
	      xy = x * y;
	      yz = y * z;
	      zx = z * x;
	      xs = x * sinAngle;
	      ys = y * sinAngle;
	      zs = z * sinAngle;
	      oneMinusCos = 1.0 - cosAngle;

	      rotMat.m[0][0] = (oneMinusCos * xx) + cosAngle;
	      rotMat.m[0][1] = (oneMinusCos * xy) - zs;
	      rotMat.m[0][2] = (oneMinusCos * zx) + ys;
	      rotMat.m[0][3] = 0.0;

	      rotMat.m[1][0] = (oneMinusCos * xy) + zs;
	      rotMat.m[1][1] = (oneMinusCos * yy) + cosAngle;
	      rotMat.m[1][2] = (oneMinusCos * yz) - xs;
	      rotMat.m[1][3] = 0.0;

	      rotMat.m[2][0] = (oneMinusCos * zx) - ys;
	      rotMat.m[2][1] = (oneMinusCos * yz) + xs;
	      rotMat.m[2][2] = (oneMinusCos * zz) + cosAngle;
	      rotMat.m[2][3] = 0.0;

	      rotMat.m[3][0] = 0.0;
	      rotMat.m[3][1] = 0.0;
	      rotMat.m[3][2] = 0.0;
	      rotMat.m[3][3] = 1.0;

	      slMatrixMultiply( result, rotMat, result );
	   }
}

function slFrustum( result, left, right, bottom, top, nearZ, farZ )
{
	   var       deltaX = right - left;
	    var       deltaY = top - bottom;
	    var       deltaZ = farZ - nearZ;
	    var       frust = new slMatrix();

	    if ( (nearZ <= 0.0) || (farZ <= 0.0) ||
	         (deltaX <= 0.0) || (deltaY <= 0.0) || (deltaZ <= 0.0) )
	         return;

	    frust.m[0][0] = 2.0 * nearZ / deltaX;
	    frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0;

	    frust.m[1][1] = 2.0 * nearZ / deltaY;
	    frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0;

	    frust.m[2][0] = (right + left) / deltaX;
	    frust.m[2][1] = (top + bottom) / deltaY;
	    frust.m[2][2] = -(nearZ + farZ) / deltaZ;
	    frust.m[2][3] = -1.0;

	    frust.m[3][2] = -2.0 * nearZ * farZ / deltaZ;
	    frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0;

	    slMatrixMultiply(result, frust, result);
}

function slPerspective(result, fovy, aspect, nearZ, farZ)
{
   var frustumW, frustumH;

   frustumH = Math.tan( fovy / 360.0 * Math.PI ) * nearZ;
   frustumW = frustumH * aspect;

   slFrustum( result, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ );
}

function slOrtho(result, left, right, bottom, top, nearZ, farZ)
{
    var       deltaX = right - left;
    var       deltaY = top - bottom;
    var       deltaZ = farZ - nearZ;
    var       ortho = new slMatrix();

    if ( (deltaX == 0.0) || (deltaY == 0.0) || (deltaZ == 0.0) )
        return;

    slMatrixLoadIdentity(ortho);
    ortho.m[0][0] = 2.0 / deltaX;
    ortho.m[3][0] = -(right + left) / deltaX;
    ortho.m[1][1] = 2.0 / deltaY;
    ortho.m[3][1] = -(top + bottom) / deltaY;
    ortho.m[2][2] = -2.0 / deltaZ;
    ortho.m[3][2] = -(nearZ + farZ) / deltaZ;

    slMatrixMultiply(result, ortho, result);
}

